<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2019/4/18 0018
 * Time: 19:39
 */
error_reporting(E_ALL);
require_once 'PEAR.php';
require_once 'Console/Getopt.php';

$daemon = false;
$port = 9500;
$max_user = 50;

$progname = basename($argv[0]);

PEAR::setErrorHandling(PEAR_ERROR_DIE, "$progname: %s\n");

$options = Console_Getopt::getopt($argv, 'dp:m:h',array());
//$options = getopt('dp:m:h');

foreach ($options[0] as $option) {
    switch ($option[0]) {
        case 'd':
            $daemon = true;
            break;
        case 'p':
            $port = $option[1];
            break;
        case 'm':
            $max_user = $option[1];
            break;
        case 'h':
        case '?':
            fwrite(STDERR, "$progname [-dh] [-p port] [-m users]
                Options:
                -d 开启守护进程 (daemon mode)
                -p post set tcp port number
                -m users set max number of users
                -h this help message
            ");
            exit(1);
    }
}

if ($daemon) {
    $pid = pcntl_fork();
    if ($pid) {
        exit(0);
    }
    posix_setsid();
}
$sock = socket_create_listen($port);
if (!$sock) {
    exit(1);
}

$shutting_down = false;
$connections = array();
$usernames = array();
$input = array();
$output = array();
$close = array();
while (true) {
    $read_fds = array_merge($connections, array($sock));
    $write_fds = array();
    $e = null;
    reset($output);
//    while (list($i, $b) = each($output)) {
//        if (strlen($b) > 0) {
//            $write_fds[] = $connections[$i];
//        }
//    }
    foreach ($output as $user => $item) {
        if (strlen($item) > 0) {
            $write_fds[] = $connections[$user];
        }

    }

    if (socket_select($read_fds, $write_fds, $e , 60)) {
//        var_dump('可读:',$read_fds);
        foreach ($read_fds as $rfd) {
            if ($rfd == $sock) {
                $new_coon = socket_accept($sock);
                $user = (int)$new_coon;
                $reject = '';
                if (count($connections) >= $max_user) {
                    $reject = "超过最大连接数\n";
                } elseif ($shutting_down) {
                    $reject = "Server shutting down\n";
                }

                $connections[$user] = $new_coon;

                $output[$user] = '';

                if ($reject) {
                    output($user, $reject);
                    $close[$user] = true;
                } else {
                    output($user, 'Welcome to the php chat Server' . PHP_EOL);
                    output($user, "userName : $user" . PHP_EOL);
                }
                $usernames[$user] = '';
                $input[$user] = '';
                continue;
            }


//            $user = (int)$rfd;


            $client_msg = socket_read($rfd, 2048, PHP_NORMAL_READ);
//            var_dump('接收客户端消息',$client_msg);
            if (!$client_msg) {
                broadcast($usernames[$user] . $user . 'lost link' . PHP_EOL);
                print  'connecttion closed on socket ' . $user . PHP_EOL;
                close($user);
                continue 2;
            }
            $input[$user] .= $client_msg;
            $tmp = substr($input[$user], -1);
            if ($tmp != "\r" && $tmp != PHP_EOL) {
                //没有结束符 还有有数据出现
                echo '没有结束符 还有有数据出现';
            }
            $line = trim($input[$user]);
            var_dump('用户名:' .$line);
            $input[$user] = '';
            if (empty($line)) {
                continue;
            }
            if (empty($usernames[$user])) {
                if (strlen($line) < 2) {
                    output($user, '用户名必须大于两个字符' . PHP_EOL);
                } else {
                    $user_name = substr($line, 0, 16);
                    if (array_search($user_name, $usernames) !== false) {
                        output($user, '该用户名已被占用，请尝试另一个 ' . PHP_EOL);
                    } else {
                        $usernames[$user] = $user_name;
                        output($user, "您的用户名是\"$user_name\"" . PHP_EOL);
                        broadcast("$user_name has logged" . PHP_EOL, $user);
                        continue;
                    }
                }
            }

            if (empty($usernames[$user])) {
                output($user, "Username : " . PHP_EOL);
            } else {
                if (strtolower($line) == '/quit') {
                    output($user, "Bye!" . PHP_EOL);
                    broadcast("$usernames[$user]  退出", $user);
                    $close[$user] = true;
                } elseif (strtolower($line) == '/shutdown') {
                    $shutting_down = true;
                    broadcast('服务器关闭' . PHP_EOL);
                } elseif (strtolower($line) == '/who') {
                    output($user, '当前的在线的用户' . PHP_EOL);
                    foreach ($usernames as $uName) {
                        output($user, $uName . PHP_EOL);
                    }
                } else {
                    $msg = '[' . $usernames[$user] . ']:Say:' . $line . PHP_EOL;
                    broadcast($msg, $user);
                    output($user, '>>>' . $line . PHP_EOL);
                }
            }
        }

        foreach ($write_fds as $wfd) {
            $user = (int)$wfd;
            if (!empty($output[$user])) {
                $w = socket_write($wfd, $output[$user]);
                if ($w == strlen($output[$user])) {
                    $output[$user] = '';
                    if (isset($close[$user])) {
                        close($user);
                    }
                } else {
                    $output[$user] = substr($output[$user], $w);
                }
            }
        }
        if ($shutting_down) {
            $may_shutdown = true;
            foreach ($output as $user => $o) {
                if (strlen($o) > 0) {
                    print  'shutdown :still data on fd ' . $user . PHP_EOL;
                    $may_shutdown = false;
                    break;
                }
            }
            if ($may_shutdown) {
                print "shutdown complete\n";
                socket_shutdown($sock);
                socket_close($sock);
                exit;
            }
        }
    }
}

function output($user, $msg)
{
    global $output;
    settype($user, 'int');
    $tmp = substr($msg, -2);
    if ($tmp{1} == "\n" && $tmp{0} != "\r") {
        $msg = substr($msg, 0, -1) . "\r\n";
    }
    $output[$user] .= $msg;
}

//广播
function broadcast($msg, $except = null)
{
    global $connections, $usernames;
    foreach ($connections as $user => $r) {
        if (empty($usernames[$user])) {
            //不要发送信息给还未登陆的用户 然后继续
            continue;
        }
        if (!$except || $except != $user) {
            //发给其他用户
            output($user, $msg);
        }
    }
}

function close($user)
{
    global $connections, $input, $output, $usernames, $close;
    socket_shutdown($connections[$user]);
    socket_close($connections[$user]);
    unset($connections[$user]);
    unset($input[$user]);
    unset($output[$user]);
    unset($usernames[$user]);
    unset($close[$user]);
}